package com.gopay.bis.nucc.expay.operation;
 
  
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.StringUtils;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.pkcs.RSAPrivateKeyStructure;

import javax.crypto.Cipher;
import java.io.*;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.security.spec.*;
import java.util.Date;
import java.util.Enumeration;

//import org.apache.commons.codec.binary.Base64;

  
/** 
 * 证书操作类 
 *  
 * @since 1.0 
 */  
public class CertificateCoder {  
    /** 
     * Java密钥库(Java Key Store，JKS)KEY_STORE 
     */  
    public static final String KEY_STORE = "JKS";  
  
    public static final String X509 = "X.509";  
  
    /** 
     * 由 KeyStore获得私钥 
     *  
     * @param keyStorePath 
     * @param keyStorePassword 
     * @param alias 
     * @param aliasPassword 
     * @return 
     * @throws Exception 
     */  
    private static PrivateKey getPrivateKey(String keyStorePath,  
            String keyStorePassword, String alias, String aliasPassword)  
            throws Exception {  
        KeyStore ks = getKeyStore(keyStorePath, keyStorePassword);  
        PrivateKey key = (PrivateKey) ks.getKey(alias,  
                aliasPassword.toCharArray());  
        return key;  
    }  
    
    /**
     * 获取RSA私钥对象
     * 
     * @param filePath
     *            RSA私钥路径
     * @param fileSuffix
     *            RSA私钥名称，决定编码类型
     * @param password
     *            RSA私钥保护密钥
     * @param keyAlgorithm
     *            密钥算法
     * @return RSA私钥对象
     * @throws Exception
     */
    @SuppressWarnings("deprecation")
    public static PrivateKey getRSAPrivateKeyByFileSuffix(String filePath, String fileSuffix, String password, String keyAlgorithm)
            throws Exception {

        String keyType = "";
        if ("keystore".equalsIgnoreCase(fileSuffix)) {
            keyType = "JKS";
        } else if ("pfx".equalsIgnoreCase(fileSuffix) || "p12".equalsIgnoreCase(fileSuffix)) {
            keyType = "PKCS12";
        } else if ("jck".equalsIgnoreCase(fileSuffix)) {
            keyType = "JCEKS";
        } else if ("pem".equalsIgnoreCase(fileSuffix) || "pkcs8".equalsIgnoreCase(fileSuffix)) {
            keyType = "PKCS8";
        } else if ("pkcs1".equalsIgnoreCase(fileSuffix)) {
            keyType = "PKCS1";
        } else {
            keyType = "JKS";
        }

        InputStream in = null;
        try {
            in = new FileInputStream(filePath);
            PrivateKey priKey = null;
            if ("JKS".equals(keyType) || "PKCS12".equals(keyType) || "JCEKS".equals(keyType)||"PKCS10".equals(keyType)) {
                KeyStore ks = KeyStore.getInstance(keyType);
                if (password != null) {
                    char[] cPasswd = password.toCharArray();
                    ks.load(in, cPasswd);
                    Enumeration<String> aliasenum = ks.aliases();
                    String keyAlias = null;
                    while (aliasenum.hasMoreElements()) {
                        keyAlias = (String) aliasenum.nextElement();
                        priKey = (PrivateKey) ks.getKey(keyAlias, cPasswd);
                        if (priKey != null)
                            break;
                    }
                }
            } else {
                BufferedReader br = new BufferedReader(new InputStreamReader(in));
                StringBuilder sb = new StringBuilder();
                String readLine = null;
                while ((readLine = br.readLine()) != null) {
                    if (readLine.charAt(0) == '-') {
                        continue;
                    } else {
                        sb.append(readLine);
                        sb.append('\r');
                    }
                }
                if ("PKCS8".equals(keyType)) {
                    PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(Base64.decodeBase64(sb.toString()));
                    KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm);
                    priKey = keyFactory.generatePrivate(priPKCS8);
                } else if ("PKCS1".equals(keyType)) {
                    RSAPrivateKeyStructure asn1PrivKey = new RSAPrivateKeyStructure((ASN1Sequence) ASN1Sequence.fromByteArray(sb.toString()
                            .getBytes()));
                    KeySpec rsaPrivKeySpec = new RSAPrivateKeySpec(asn1PrivKey.getModulus(), asn1PrivKey.getPrivateExponent());
                    KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm);
                    priKey = keyFactory.generatePrivate(rsaPrivKeySpec);
                }
            }

            return priKey;
        } catch (FileNotFoundException e) {
            throw new Exception("私钥路径文件不存在");
        } catch (KeyStoreException e) {
            throw new Exception("获取KeyStore对象异常");
        } catch (IOException e) {
            throw new Exception("读取私钥异常");
        } catch (NoSuchAlgorithmException e) {
            throw new Exception("生成私钥对象异常");
        } catch (CertificateException e) {
            throw new Exception("加载私钥密码异常");
        } catch (UnrecoverableKeyException e) {
            throw new Exception("生成私钥对象异常");
        } catch (InvalidKeySpecException e) {
            throw new Exception("生成私钥对象异常");
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (IOException e) {
            }
        }
    }

  
    /** 
     * 由 Certificate获得公钥 
     *  
     * @param certificatePath 
     * @return 
     * @throws Exception 
     */  
    private static PublicKey getPublicKey(String certificatePath)  
            throws Exception {  
        Certificate certificate = getCertificate(certificatePath);  
        PublicKey key = certificate.getPublicKey();  
        return key;  
    }  
  
    /** 
     * 获得Certificate 
     *  
     * @param certificatePath 
     * @return 
     * @throws Exception 
     */  
    private static Certificate getCertificate(String certificatePath)  
            throws Exception {  
        CertificateFactory certificateFactory = CertificateFactory  
                .getInstance(X509);  
        FileInputStream in = new FileInputStream(certificatePath);
        Certificate	certificate = certificateFactory.generateCertificate(in);  
        in.close();
        return certificate;  
    }  
  
    /** 
     * 获得Certificate 
     *  
     * @param keyStorePath 
     * @param keyStorePassword 
     * @param alias 
     * @return 
     * @throws Exception 
     */  
    private static Certificate getCertificate(String keyStorePath,  
            String keyStorePassword, String alias) throws Exception {  
        KeyStore ks = getKeyStore(keyStorePath, keyStorePassword);  
        Certificate certificate = ks.getCertificate(alias);  
  
        return certificate;  
    }  
  
    /** 
     * 获得KeyStore 
     *  
     * @param keyStorePath 
     * @param password 
     * @return 
     * @throws Exception 
     */  
    private static KeyStore getKeyStore(String keyStorePath, String password)  
            throws Exception {  
        FileInputStream is = new FileInputStream(keyStorePath);  
        KeyStore ks = KeyStore.getInstance(KEY_STORE);  
        ks.load(is, password.toCharArray());  
        is.close();  
        return ks;  
    }  
  
    /** 
     * 私钥加密 
     *  
     * @param data 
     * @param keyStorePath 
     * @param keyStorePassword 
     * @param alias 
     * @param aliasPassword 
     * @return 
     * @throws Exception 
     */  
    public static byte[] encryptByPrivateKey(byte[] data, String keyStorePath,  
            String keyStorePassword, String alias, String aliasPassword)  
            throws Exception {  
        // 取得私钥  
        PrivateKey privateKey = getPrivateKey(keyStorePath, keyStorePassword,  
                alias, aliasPassword);  
  
        // 对数据加密  
        Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());  
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);  
  
        return cipher.doFinal(data);  
  
    }  
  
    /** 
     * 私钥解密 
     *  
     * @param data 
     * @param keyStorePath 
     * @param alias 
     * @param keyStorePassword 
     * @param aliasPassword 
     * @return 
     * @throws Exception 
     */  
    public static byte[] decryptByPrivateKey(byte[] data, String keyStorePath,  
            String alias, String keyStorePassword, String aliasPassword)  
            throws Exception {  
        // 取得私钥  
        PrivateKey privateKey = getPrivateKey(keyStorePath, keyStorePassword,  
                alias, aliasPassword);  
  
        // 对数据加密  
        Cipher cipher = Cipher.getInstance(privateKey.getAlgorithm());  
        cipher.init(Cipher.DECRYPT_MODE, privateKey);  
  
        return cipher.doFinal(data);  
  
    }  
  
    /** 
     * 公钥加密 
     *  
     * @param data 
     * @param certificatePath 
     * @return 
     * @throws Exception 
     */  
    public static byte[] encryptByPublicKey(byte[] data, String certificatePath)  
            throws Exception { 
        
        
  
        // 取得公钥  
        PublicKey publicKey = getPublicKey(certificatePath);  
        // 对数据加密  
        Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());  
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);  
  
        return cipher.doFinal(data);  
  
    }  
    
    public static byte[] encryptByPublicKey(String key, String certificatePath)  
            throws Exception { 
        
        byte[] data = key.getBytes("utf-8");
  
        // 取得公钥  
        PublicKey publicKey = getPublicKey(certificatePath);  
        // 对数据加密  
        Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());  
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);  
  
        return cipher.doFinal(data);  
  
    }  
  
    /** 
     * 公钥解密 
     *  
     * @param data 
     * @param certificatePath 
     * @return 
     * @throws Exception 
     */  
    public static byte[] decryptByPublicKey(byte[] data, String certificatePath)  
            throws Exception {  
        // 取得公钥  
        PublicKey publicKey = getPublicKey(certificatePath);  
  
        // 对数据加密  
        Cipher cipher = Cipher.getInstance(publicKey.getAlgorithm());  
        cipher.init(Cipher.DECRYPT_MODE, publicKey);  
  
        return cipher.doFinal(data);  
  
    }

    /**
     * 私钥解密
     *
     * @param data
     * @param certificatePath
     * @return
     * @throws Exception
     */
    public static byte[] decryptByPrivateKey(byte[] data, String certificatePath)
            throws Exception {
        PrivateKey key = getRSAPrivateKeyByFileSuffix(certificatePath, "pem", null, "RSA");
        // 对数据加密
        Cipher cipher = Cipher.getInstance(key.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, key);
        return cipher.doFinal(data);
    }

    /** 
     * 验证Certificate 
     *  
     * @param certificatePath 
     * @return 
     */  
    public static boolean verifyCertificate(String certificatePath) {  
        return verifyCertificate(new Date(), certificatePath);  
    }  
  
    /** 
     * 验证Certificate是否过期或无效 
     *  
     * @param date 
     * @param certificatePath 
     * @return 
     */  
    public static boolean verifyCertificate(Date date, String certificatePath) {  
        boolean status = true;  
        try {  
            // 取得证书  
            Certificate certificate = getCertificate(certificatePath);  
            // 验证证书是否过期或无效  
            status = verifyCertificate(date, certificate);  
        } catch (Exception e) {  
            status = false;  
        }  
        return status;  
    }  
  
    /** 
     * 验证证书是否过期或无效 
     *  
     * @param date 
     * @param certificate 
     * @return 
     */  
    private static boolean verifyCertificate(Date date, Certificate certificate) {  
        boolean status = true;  
        try {  
            X509Certificate x509Certificate = (X509Certificate) certificate;  
            x509Certificate.checkValidity(date);  
        } catch (Exception e) {  
            status = false;  
        }  
        return status;  
    }  
  
    /** 
     * 签名 
     *  
     * @param keyStorePath 
     * @param alias 
     * @param keyStorePassword 
     * @param aliasPassword 
     * @return 
     * @throws Exception 
     */  
    @SuppressWarnings("restriction")
	public static byte[] sign(byte[] sign, String keyStorePath, String alias,  
            String keyStorePassword, String aliasPassword) throws Exception {  
        // 获得证书   // 根证书
        X509Certificate x509Certificate = (X509Certificate) getCertificate(  
                keyStorePath, keyStorePassword, alias);  
  
        // 取得私钥  
        PrivateKey privateKey = getPrivateKey(keyStorePath, keyStorePassword,  
                alias, aliasPassword);  
  
        // 构建签名 SHA256withRSA 
        Signature signature = Signature.getInstance(x509Certificate  
                .getSigAlgName());  
        signature.initSign(privateKey);  
        signature.update(sign);
        return  signature.sign();
        
    }  
    
    public static byte[] sign(String plainMsg, String keyStorePath, String alias,  
            String keyStorePassword, String aliasPassword) throws Exception {  
        byte[] sign =(plainMsg.hashCode()+"").getBytes("utf-8");
        // 获得证书   // 根证书
        X509Certificate x509Certificate = (X509Certificate) getCertificate(  
                keyStorePath, keyStorePassword, alias);  
  
        // 取得私钥  
        PrivateKey privateKey = getPrivateKey(keyStorePath, keyStorePassword,  
                alias, aliasPassword);  
  
        // 构建签名 SHA256withRSA 
        Signature signature = Signature.getInstance(x509Certificate  
                .getSigAlgName());  
        signature.initSign(privateKey);  
        signature.update(sign);
        return  signature.sign();
        
    } 
    
    public static byte[] sign(String plainMsg,String path,String signAlgorithm) throws Exception{
        PrivateKey hzfPriKey = getRSAPrivateKeyByFileSuffix(path, "pem", null, "RSA");
        Signature signature = Signature.getInstance(signAlgorithm);
        signature.initSign(hzfPriKey);
        signature.update(StringUtils.getBytesUtf8(plainMsg));
        byte[] signBytes = signature.sign();
        return signBytes;        
    }
    

    /** 
     * 验证签名 
     *  
     * @param data 
     * @param sign 
     * @param certificatePath 
     * @return 
     * @throws Exception 
     */  
    public static boolean verify(byte[] data, byte[] sign,  
            String certificatePath) throws Exception {  
        // 获得证书  
        X509Certificate x509Certificate = (X509Certificate) getCertificate(certificatePath);  
        // 获得公钥  
        PublicKey publicKey = x509Certificate.getPublicKey();  
        // 构建签名  
        Signature signature = Signature.getInstance(x509Certificate  
                .getSigAlgName());  
        signature.initVerify(publicKey);  
        signature.update(data);  
  
        return signature.verify(sign);  
  
    } 
    
    public static boolean verify(String plain, String signMsg,  
            String certificatePath) throws Exception {  
        byte[] data =StringUtils.getBytesUtf8(plain);
        byte[] sign =Base64.decodeBase64(signMsg);
        // 获得证书
        X509Certificate x509Certificate = (X509Certificate) getCertificate(certificatePath);  
        // 获得公钥  
        PublicKey publicKey = x509Certificate.getPublicKey();  
        // 构建签名  
        Signature signature = Signature.getInstance(x509Certificate  
                .getSigAlgName());  
        signature.initVerify(publicKey);  
        signature.update(data);  
        return signature.verify(sign);  
  
    } 
  
    /** 
     * 验证Certificate 
     *  
     * @param keyStorePath 
     * @param keyStorePassword 
     * @param alias 
     * @return 
     */  
    public static boolean verifyCertificate(Date date, String keyStorePath,  
            String keyStorePassword, String alias) {  
        boolean status = true;  
        try {  
            Certificate certificate = getCertificate(keyStorePath,  
                    keyStorePassword, alias);  
            status = verifyCertificate(date, certificate);  
        } catch (Exception e) {  
            status = false;  
        }  
        return status;  
    }  
  
    /** 
     * 验证Certificate 
     *  
     * @param keyStorePath 
     * @param keyStorePassword 
     * @param alias 
     * @return 
     */  
    public static boolean verifyCertificate(String keyStorePath,  
            String keyStorePassword, String alias) {  
        return verifyCertificate(new Date(), keyStorePath, keyStorePassword,  
                alias);  
    }  
    
    /**
     * 构建私钥
     * @param key
     * @return
     * @throws NoSuchAlgorithmException
     * @throws InvalidKeySpecException
     */
    public static  PrivateKey generatePrivateKey(byte[] key) throws NoSuchAlgorithmException, InvalidKeySpecException {  
        KeySpec keySpec = new PKCS8EncodedKeySpec(key);  
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");  
        return keyFactory.generatePrivate(keySpec);  
    }  
    
    /**
     * 构建公钥
     * @param key
     * @return
     * @throws InvalidKeySpecException
     * @throws NoSuchAlgorithmException
     */
    public static PublicKey geneneratePublicKey(byte[] key) throws InvalidKeySpecException, NoSuchAlgorithmException{  
        KeySpec keySpec = new X509EncodedKeySpec(key);  
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");  
        return keyFactory.generatePublic(keySpec);  
    }  
}  